import json
import traceback
from collections import OrderedDict

from flask import current_app, request
from flask_restx import abort, reqparse

from app.common.range_manager import RangeManager
from app.models import courseModel, userModel, rangeModel, switchModel, tb_chapter_courseware
from app.models.coursewareModel import Courseware
from tools import db
from tools import redis_conn as redis_store
from tools.basic_tools import Tools
from utils.utils import base_detail, get_task_status, list_dict_duplicate_removal
from template import l2_network_tpl, l3_network_tpl, vm_tpl, basic_tpl, router_tpl, eip_tpl, security_group_tpl

from .base_view import OrganizerBase
from werkzeug.exceptions import BadRequest
from app.api.v1.organizer.rangeEnv import RangeStackTemplate
from app.common.transaction_manager import create_transaction


class Course(OrganizerBase):
    def get_course(self, id=None, name=None):
        course_info = None
        if id:
            course_info = courseModel.Course.query.filter_by(id=id).first()
        elif name:
            course_info = courseModel.Course.query.filter_by(name=name).first()
        return course_info


class Chapter(OrganizerBase):
    def get_chapter(self, id=None, name=None):
        chapter_info = None
        if id:
            chapter_info = courseModel.Chapter.query.filter_by(id=id).first()
        elif name:
            chapter_info = courseModel.Chapter.query.filter_by(name=name).first()
        return chapter_info


class CourseManage(Course):
    def get(self, uuid=None):
        params = request.args
        need_status = params.get("need_status", "0")
        course_info = self.get_course(id=uuid)
        if not course_info:
            abort(400, message="该课程不存在")
        if need_status == "1":
            return {"code": 200, "data": course_info.to_status_dict()}
        return {"code": 200, "data": course_info.to_dict()}

    def post(self):
        args = request.json
        if not all(
            [
                args.get("course_name"),
                args.get("course_uuid"),
                args.get("chapter_coursewares"),
                args.get("users"),
            ]
        ):
            abort(400, message="参数错误")

        if self.get_course(name=args["course_name"]):
            abort(400, message="课程名已存在")

        with db.auto_commit():
            # 新建课程，关联章节，关联用户
            course_obj = courseModel.Course(
                id=args["course_uuid"],
                name=args["course_name"],
                describe=args.get("describe", None),
                user_id=self.user.id,
            )
            db.session.add(course_obj)

            # 新建章节，关联课件
            sort_num = 0
            for chapter in args["chapter_coursewares"]:
                sort_num +=1
                chapter_obj = courseModel.Chapter(
                    id=chapter["uuid"], name=chapter["name"], sort_num=sort_num
                )
                db.session.add(chapter_obj)

                # 将指定的课件添加到章节下面, 将指定的章节添加到课程下面
                for child in chapter["child"]:
                    courseware = Courseware.query.get(int(child["courseware_id"]))
                    if not courseware:
                        abort(400, message="课件不存在")
                    chapters = courseModel.Chapter.query.get(chapter["uuid"])
                    if not chapters:
                        abort(400, message="章节不存在")
                    # 不能添加无拓扑的虚拟机课件 change: 21.12.29
                    if courseware.type == "3" and not courseware.range_uuid:
                        abort(400, message=f"课件: {courseware.name} 无拓扑, 无法添加")
                    if courseware not in chapters.coursewares:
                        chapters.coursewares.append(courseware)
                    chapters.course_id = args["course_uuid"]

            # 关联用户和课程
            course = courseModel.Course.query.get(args["course_uuid"])
            if not course:
                abort(400, message="课程不存在")
            for user in args["users"]:
                user_info = userModel.User.query.filter_by(id=user["id"]).first()
                if course not in user_info.courses:
                    user_info.courses.append(course)

            current_app.logger.info(
                self.user.username + "---action--add--course:" + args["course_name"]
            )
            return {"code": 200, "data": course_obj.to_dict()}

    def put(self):
        args = request.json
        if not all(
            [
                args.get("course_uuid"),
                args.get("chapter_coursewares"),
                args.get("users"),
            ]
        ):
            abort(400, message="参数错误")

        with db.auto_commit():
            # 根据uuid获取课程信息并删除
            course_info = self.get_course(id=args["course_uuid"])
            db.session.delete(course_info)

            # 新建课程，关联章节，关联用户
            course_obj = courseModel.Course(
                id=args["course_uuid"],
                name=args["course_name"],
                describe=args.get("describe", None),
                user_id=self.user.id,
            )
            db.session.add(course_obj)

            # 新建章节，关联课件
            sort_num = 0
            for chapter in args["chapter_coursewares"]:
                sort_num += 1
                chapter_obj = courseModel.Chapter(
                    id=chapter["uuid"], name=chapter["name"], sort_num=sort_num
                )
                db.session.add(chapter_obj)

                # 将指定的课件添加到章节下面, 将指定的章节添加到课程下面
                for child in chapter["child"]:
                    courseware = Courseware.query.get(int(child["courseware_id"]))
                    if not courseware:
                        abort(400, message="课件不存在")
                    chapters = courseModel.Chapter.query.get(chapter["uuid"])
                    if not chapters:
                        abort(400, message="章节不存在")
                    if courseware not in chapters.coursewares:
                        chapters.coursewares.append(courseware)
                    chapters.course_id = args["course_uuid"]

            # 关联用户和课程
            course = courseModel.Course.query.get(args["course_uuid"])
            if not course:
                abort(400, message="课程不存在")
            for user in args["users"]:
                user_info = userModel.User.query.filter_by(id=user["id"]).first()
                if course not in user_info.courses:
                    user_info.courses.append(course)

            current_app.logger.info(
                self.user.username + "---action--put--course:" + args["course_name"]
            )
            return {"code": 200, "data": course_obj.to_dict()}

    def delete(self, uuid=None):
        with db.auto_commit():
            course_info = self.get_course(id=uuid)
            if not course_info:
                abort(400, message="course not exist")
            db.session.delete(course_info)
            return {"code": 200, "data": "删除成功"}


class CourseList(Course):
    def get(self):
        params = request.args
        page = params.get("page", 1)
        page_size = params.get("pageSize", 10)
        system = params.get("system")
        keyword = params.get("keyword")
        try:
            page = int(page)
            page_size = int(page_size)
        except Exception:
            page = 1
            page_size = 10

        if page_size >= 50:
            abort(400, message="每页数量不能超过50")

        if system not in ["0", "1"]:
            abort(400, message="参数错误")

        try:
            filters, paginate, my_paginate = [], None, None
            if keyword:
                # 添加关键词的检索选项
                filters.append(courseModel.Course.name.contains(keyword))

            if system == "1":
                paginate = (
                    courseModel.Course.query.filter(*filters)
                    .filter_by(isSystem=1)
                    .order_by(courseModel.Course.update_time.desc())
                    .paginate(page, page_size, error_out=False)
                )
            else:
                paginate = (
                    courseModel.Course.query.filter(*filters)
                    .filter_by(user_id=self.user.id, isSystem=0)
                    .order_by(courseModel.Course.update_time.desc())
                    .paginate(page, page_size, error_out=False)
                )
                my_paginate = (
                    self.user.courses.filter(*filters)
                    .order_by(courseModel.Course.update_time.desc())
                    .paginate(page, page_size, error_out=False)
                )

            info = {}
            info["current_page"] = page
            data_list = []
            if paginate and paginate.items and len(paginate.items) > 0:
                for content in paginate.items:
                    data_list.append(content.to_dict())
            if my_paginate and my_paginate.items and len(my_paginate.items) > 0:
                for content in my_paginate.items:
                    data_list.append(content.to_dict())
            info["data"] = list_dict_duplicate_removal(data_list)
            info = base_detail(info, paginate)
            return {"code": 200, "data": info}

        except Exception as e:
            current_app.logger.exception(e)
            abort(500, message="参数错误")


class CourseJoin(Course):
    def post(self):
        args = request.json
        if not all([args.get("course_uuid"), args.get("users")]):
            abort(400, message="参数错误")

        with db.auto_commit():
            course = courseModel.Course.query.get(args["course_uuid"])
            if not course:
                abort(400, message="课程不存在")

            course.user_id = self.user.id
            db.session.add(course)
            for user_info in args["users"]:
                user = userModel.User.query.filter_by(id=user_info["id"]).first()
                if course not in user.courses:
                    user.courses.append(course)
            return {"code": 200, "data": "操作成功"}


class CourseStatus(Course):
    
    # @property
    # def load_cloud_object(self):
    #     return WebHookManager()
    
    
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument(
            "course_uuid", type=str, help="Bad choice: {error_msg}", required=True
        )
        parser.add_argument(
            "status", choices=("0", "1"), help="Bad choice: {error_msg}", required=True
        )
        args = parser.parse_args()

        if not self.get_course(id=args["course_uuid"]):
            abort(400, message="课程不存在")
        try:
            course_info = self.get_course(id=args["course_uuid"]).to_status_dict()
        except BadRequest as e:
            abort(400, message=str(e))
        if args["status"] == "0":
            self.close_course(args.course_uuid, course_info)
        else:
            self.open_course(args.course_uuid, course_info)
        return {"code": 200, "data": "操作成功"}
    
    
    def open_course(self, course_uuid, course_info):
        # 开启课件
        try:
            courseModel.Course.query.filter_by(id=course_uuid).update(dict(status='1'))
            chapter_coursewares = course_info["chapter_coursewares"]
            for chapter in chapter_coursewares:
                    for courseware in chapter["child"]:
                        db.session.execute(
                                f"""update api_chapter_courseware set env_status='1'
                                where courseware_id={courseware["courseware_id"]} and chapter_id='{chapter["uuid"]}'"""
                            )
                        if courseware["range_uuid"]:
                            range_manager = RangeManager(range_uuid=courseware["range_uuid"], ignore_private=True)
                            if range_manager.range_model.Range.is_draft:
                                abort(400, message="场景未确认修改，请联系管理员或教师操作")
                            # self.create_template(courseware["range_uuid"])
            db.session.commit()
            return {"code": 200, "data": "操作成功"}
        except Exception:
            db.session.rollback()
            print(traceback.print_exc())
            
            
    
    def close_course(self, course_uuid,  course_info):
        # 关闭课件
        try:
            courseModel.Course.query.filter_by(id=course_uuid).update(dict(status='0'))
            chapter_coursewares = course_info["chapter_coursewares"]
            for chapter in chapter_coursewares:
                if "child" in chapter and len(chapter["child"]) != 0:
                    for courseware in chapter["child"]:
                        db.session.execute(
                                f"""update api_chapter_courseware set env_status='0'
                                where courseware_id={courseware["courseware_id"]} and chapter_id='{chapter["uuid"]}'"""
                            )
                        if courseware["range_uuid"]:
                            self.del_template(range_uuid=courseware["range_uuid"])
            db.session.commit()
            course_users = course_info["users"]
            for user in course_users:
                redis_store.delete(f"{course_uuid}_" + str(user["id"]))
            return {"code": 200, "data": "操作成功"}
        except Exception as e:
            db.session.rollback()
            current_app.logger.exception(e)
        
            
            
    def create_template(self, range_uuid):
        
        template_obj = rangeModel.StackTemplate.query.filter_by(range_uuid=range_uuid, is_system="1").first()
        # 生成资源模板
        if template_obj:
            abort(400, message="已存在场景")
        range_data = RangeStackTemplate._building_data(range_uuid)
        # 1. 二层网络
        range_model = rangeModel.RangeLinkModule.query.filter_by(range_uuid=range_uuid).first()
        # 构建json
        tpl_l2_pool_uuid = range_model.vxlan_pool_id
        l2_uuid = Tools.get_uuid4_no_line()
        zone_uuid = current_app.config["ZSTACK_CLOUD"]["zone_uuid"]
        pool_uuid = tpl_l2_pool_uuid

        l2_network = json.loads(l2_network_tpl.l2_network_str.format(
            l2_uuid=l2_uuid, zone_uuid=zone_uuid, pool_uuid=pool_uuid))
        basic_json = basic_tpl.basic_json
        device_json = l2_network
        router_to_vm_list = []
        for vpc_uuid, value in range_data.items():
            # 构建交换机
            # 2.交换机
            switch_obj = switchModel.Switchs.query.filter_by(range_uuid=range_uuid, subnet_id=vpc_uuid).first()
            vpc_cidr = switch_obj.cidr
            vpc_network = json.loads(l3_network_tpl.vpc_network_str.format(
                vpc_uuid=vpc_uuid, l2_uuid=l2_uuid))
            vpc_network_cidr = json.loads(l3_network_tpl.vpc_network_cidr.format(
                vpc_uuid=vpc_uuid, vpc_cidr=vpc_cidr))
            device_json = dict(device_json, **vpc_network, **vpc_network_cidr)
            # 路由器
            for router_num, (router_key, router_value) in enumerate(value.get("router_gw", {}).items()):
                router_offer_uuid = current_app.config["ZSTACK_CLOUD"]["router_uuid"]
                router_json, attach_router_json, router_to_vm = RangeStackTemplate._create_router_template(router_key, router_key, router_offer_uuid, router_num, vpc_uuid)
                device_json = dict(device_json, **router_json, **attach_router_json)
                router_to_vm_list.append(router_to_vm)
            # 虚拟机
            for vm_key, vm_value in value.get("vm", {}).items():
                vm_json = RangeStackTemplate._create_vm_template(vpc_uuid, vm_key, vm_value, router_to_vm_list, start_vm=False)
                device_json = dict(device_json, **vm_json)
            # 安全组
            basic_json["Resources"] = device_json
        resource_id = Tools.get_uuid4_no_line()
        self.load_cloud_object.add_stack_template(range_uuid, basic_json, resource_id, range_uuid, is_system=1)
        return
        
    def del_template(self, range_uuid):
        #删除改资源下的所有课题资源
        action_params = OrderedDict()
        range_list = rangeModel.ActiveRange.query.filter_by(range_uuid=range_uuid).all()
        for range  in range_list:
            # 查找交换机、路由去、虚拟机
            rangeVmList = rangeModel.ActiveRangeVm.query.filter_by(activeRange_range_uuid=range.activeRange_range_uuid).all()

            for vm in rangeVmList:
                if vm.op_vm_uuid:
                    action_params[f"del_vm_destroy-{vm.op_vm_uuid}"] = {"vm_uuid": vm.op_vm_uuid}
                    action_params[f"del_vm_expunge-{vm.op_vm_uuid}"] = {"vm_uuid": vm.op_vm_uuid}
                # 删除虚拟ip
                if vm.is_external:
                    action_params[f"del_vips_ip-{vm.op_vm_uuid}"] = {"vip_uuid": vm.eip_uuid}
                
            # 删除路由器
            route_list = switchModel.ActiveRoutes.query.filter_by(
                range_uuid=range.activeRange_range_uuid).all()
            for route in route_list:
                action_params[f"del_vm_destroy-{route.op_route}"] = {"vm_uuid": route.op_route}
            action_params["del_l2_network"] = {"network_id": range.network_id}
            db.session.delete(range)
        create_transaction(action_params, range_uuid)
        db.session.commit()
            
        
        # template_obj = rangeModel.StackTemplate.query.filter_by(range_uuid=range_uuid, is_system="1").first()
        # # 删除资源模板
        # self.load_cloud_object.del_stack_template(
        #     template_obj.stack_uuid
        # )
        # # 删除资源栈
        # resource_obj = rangeModel.StackResource.query.filter_by(stack_uuid=template_obj.stack_uuid).first()
        # if resource_obj:
        #     self.load_cloud_object.del_resource_stack(resource_obj.resource_stack_uuid)
        # rangeModel.StackTemplate.query.filter_by(range_uuid=range_uuid, is_system="1").delete()
        # db.session.commit()
        return 
